﻿using System;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace Dev2419a_LunarLander3D
{
    class Terreno
    {
        private int terrainWidth;
        private int terrainHeight;
        private float[,] heightData;

        private VertexBuffer myVertexBuffer;
        private IndexBuffer myIndexBuffer;
        private VertexPositionColorNormal[] vertices;
        private int[] indices;

        private GraphicsDevice device;

        public Terreno(GraphicsDevice device,Texture2D HeightMap)
        {
            //
            this.device = device;
            LoadHeightData(HeightMap);

            SetUpIndices();
            SetUpVertices();
            CalculateNormals();

            CopyToBuffers();
        }
        /// <summary>
        /// Monta e retorna os objetos e arrays do terreno
        /// </summary>
        /// <param name="heightData">out heightData - array com as altitudes</param>
        /// <param name="myVertexBuffer">out myVertexBuffer - array com as altitudes</param>
        /// <param name="myIndexBuffer">out myIndexBuffer - array com as altitudes</param>
        /// <param name="vertices">out vertices - Array de pontos (Classe Nova) com cor e normal</param>
        /// <param name="indices">out indices - array com a organização dos pontos (normal pra fora)</param>
        public void montaTerreno(out float[,] heightData, out VertexBuffer myVertexBuffer, out IndexBuffer myIndexBuffer, out VertexPositionColorNormal[] vertices, out int[] indices)
        {
            heightData = this.heightData;
            myVertexBuffer = this.myVertexBuffer;
            myIndexBuffer = this.myIndexBuffer;
            vertices = this.vertices;
            indices = this.indices;
        }

        private void SetUpIndices()
        {
            indices = new int[(terrainWidth - 1) * (terrainHeight - 1) * 6];
            int counter = 0;
            for (int y = 0; y < terrainHeight - 1; y++)
            {
                for (int x = 0; x < terrainWidth - 1; x++)
                {
                    int lowerLeft = x + y * terrainWidth;
                    int lowerRight = (x + 1) + y * terrainWidth;
                    int topLeft = x + (y + 1) * terrainWidth;
                    int topRight = (x + 1) + (y + 1) * terrainWidth;

                    indices[counter++] = topLeft;
                    indices[counter++] = lowerRight;
                    indices[counter++] = lowerLeft;

                    indices[counter++] = topLeft;
                    indices[counter++] = topRight;
                    indices[counter++] = lowerRight;
                }
            }
        }

        private void SetUpVertices()
        {
            float minHeight = float.MaxValue;
            float maxHeight = float.MinValue;
            for (int x = 0; x < terrainWidth; x++)
            {
                for (int y = 0; y < terrainHeight; y++)
                {
                    if (heightData[x, y] < minHeight)
                        minHeight = heightData[x, y];
                    if (heightData[x, y] > maxHeight)
                        maxHeight = heightData[x, y];
                }
            }

            vertices = new VertexPositionColorNormal[terrainWidth * terrainHeight];
            for (int x = 0; x < terrainWidth; x++)
            {
                for (int z = 0; z < terrainHeight; z++)
                {
                    //vertices[x + y * terrainWidth].Position = new Vector3(x, 0, -y);
                    vertices[x + z * terrainWidth].Position = new Vector3(x, heightData[x, z], -z);

                    //vertices[x + y * terrainWidth].Color = Color.WhiteSmoke;


                    if (heightData[x, z] < minHeight + (maxHeight - minHeight) / 4)
                        vertices[x + z * terrainWidth].Color = Color.DarkGreen;

                    else if (heightData[x, z] < minHeight + (maxHeight - minHeight) * 2 / 4)
                        vertices[x + z * terrainWidth].Color = Color.CornflowerBlue;

                    else if (heightData[x, z] < minHeight + (maxHeight - minHeight) * 3 / 4)
                        vertices[x + z * terrainWidth].Color = Color.White;

                    else
                        vertices[x + z * terrainWidth].Color = Color.SpringGreen;

                }
            }
        }

        private void LoadHeightData(Texture2D heightMap)
        {
            terrainWidth = heightMap.Width;
            terrainHeight = heightMap.Height;

            Color[] heightMapColors = new Color[terrainWidth * terrainHeight];
            heightMap.GetData(heightMapColors);

            heightData = new float[terrainWidth, terrainHeight];
            for (int x = 0; x < terrainWidth; x++)
                for (int y = 0; y < terrainHeight; y++)
                    heightData[x, y] = heightMapColors[x + y * terrainWidth].R / 5.0f;
        }

        private void CalculateNormals()
        {
            for (int i = 0; i < vertices.Length; i++)
                vertices[i].Normal = new Vector3(0, 0, 0);

            for (int i = 0; i < indices.Length / 3; i++)
            {
                int index1 = indices[i * 3];
                int index2 = indices[i * 3 + 1];
                int index3 = indices[i * 3 + 2];

                Vector3 side1 = vertices[index1].Position - vertices[index3].Position;
                Vector3 side2 = vertices[index1].Position - vertices[index2].Position;
                Vector3 normal = Vector3.Cross(side1, side2);

                vertices[index1].Normal += normal;
                vertices[index2].Normal += normal;
                vertices[index3].Normal += normal;
            }
            for (int i = 0; i < vertices.Length; i++)
                vertices[i].Normal.Normalize();
        }

        private void CopyToBuffers()
        {
            myVertexBuffer = new VertexBuffer(device, VertexPositionColorNormal.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
            myVertexBuffer.SetData(vertices);
            myIndexBuffer = new IndexBuffer(device, typeof(int), indices.Length, BufferUsage.WriteOnly);
            myIndexBuffer.SetData(indices);
        }

        public float getTerrainHeight(Vector3 pos){
            int x = (int)pos.X;
            int y = (int)pos.Z*-1;

            if (x >= 0 && x <= 128 && y >= 0 && y < 128)
                return heightData[x, y];
            else
                return 0f;
        }
    }
}
